package main

/*
Copyright 2021-2025 The k8gb Contributors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Generated by GoLic, for more details see: https://github.com/AbsaOSS/golic
*/

import (
	"context"
	"os"

	"github.com/k8gb-io/k8gb/controllers/resolver"

	k8gbv1beta1 "github.com/k8gb-io/k8gb/api/v1beta1"
	"github.com/k8gb-io/k8gb/controllers"
	boot "github.com/k8gb-io/k8gb/controllers/bootstrap"
	"github.com/k8gb-io/k8gb/controllers/logging"
	"github.com/k8gb-io/k8gb/controllers/providers/dns"
	"github.com/k8gb-io/k8gb/controllers/providers/metrics"
	"github.com/k8gb-io/k8gb/controllers/tracing"
	istio "istio.io/client-go/pkg/apis/networking/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
	ctrl "sigs.k8s.io/controller-runtime"
	metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
	"sigs.k8s.io/controller-runtime/pkg/scheme"
	externaldnsApi "sigs.k8s.io/external-dns/apis/v1alpha1"
	gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1"
	gatewayapiv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
	gatewayapiv1alpha3 "sigs.k8s.io/gateway-api/apis/v1alpha3"
	// +kubebuilder:scaffold:imports
)

var (
	runtimescheme = runtime.NewScheme()
	version       = "development"
	commit        = "none"
)

func init() {
	utilruntime.Must(clientgoscheme.AddToScheme(runtimescheme))
	utilruntime.Must(k8gbv1beta1.AddToScheme(runtimescheme))
	utilruntime.Must(istio.AddToScheme(runtimescheme))
	utilruntime.Must(gatewayapiv1.Install(runtimescheme))
	utilruntime.Must(gatewayapiv1alpha2.Install(runtimescheme))
	utilruntime.Must(gatewayapiv1alpha3.Install(runtimescheme))
	// +kubebuilder:scaffold:scheme
}

func main() {
	if err := run(); err != nil {
		os.Exit(1)
	}
}

func run() error {
	var f *dns.ProviderFactory
	r := resolver.NewResolver()
	config, err := r.ResolveOperatorConfig()
	deprecations := r.GetDeprecations()
	// Initialize desired log or default log in case of configuration failed.
	logging.Init(config)
	log := logging.Logger()
	log.Info().
		Str("version", version).
		Str("commit", commit).
		Msg("k8gb info")
	if err != nil {
		log.Err(err).Msg("Can't resolve environment variables")
		return err
	}
	log.Debug().
		Interface("config", config).
		Msg("Resolved config")

	ctrl.SetLogger(logging.NewLogrAdapter(log))

	log.Info().Msg("Reading external IPs from cluster")
	bootstrap, err := boot.GetBootstrap(context.TODO(), config, ctrl.GetConfigOrDie())
	if err != nil {
		if config.CoreDNSServiceType == corev1.ServiceTypeLoadBalancer {
			log.Err(err).Msg("Can't resolve external IPs")
			return err
		}
		log.Err(err).Msg("Can't resolve ingress IPs")
		return err
	}
	log.Info().Msgf("Found External IP's: %s", bootstrap)
	config.DelegationZones.SetIPs(bootstrap.IPs)

	mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
		Scheme: runtimescheme,
		Metrics: metricsserver.Options{
			BindAddress: config.MetricsAddress,
		},
		LeaderElection:   false,
		LeaderElectionID: "8020e9ff.absa.oss",
	})
	if err != nil {
		log.Err(err).Msg("Unable to create k8gb operator manager")
		return err
	}

	for _, d := range deprecations {
		log.Warn().Msg(d)
	}

	log.Info().Msg("Registering components")

	// Add external-dns DNSEndpoints resource
	// https://github.com/operator-framework/operator-sdk/blob/master/doc/user-guide.md#adding-3rd-party-resources-to-your-operator
	schemeBuilder := &scheme.Builder{GroupVersion: schema.GroupVersion{Group: "externaldns.k8s.io", Version: "v1alpha1"}}
	schemeBuilder.Register(&externaldnsApi.DNSEndpoint{}, &externaldnsApi.DNSEndpointList{})
	if err := schemeBuilder.AddToScheme(mgr.GetScheme()); err != nil {
		log.Err(err).Msg("Unable to register ExternalDNS resource schemas")
		return err
	}

	log.Info().Msg("Starting metrics")
	metrics.Init(config)
	defer metrics.Metrics().Unregister()
	err = metrics.Metrics().Register()
	if err != nil {
		log.Err(err).Msg("Unable to register metrics")
		return err
	}
	// Initialize all metrics with zero values so they appear in Prometheus dashboards
	metrics.Metrics().InitializeZeroValues()

	log.Info().Msg("Resolving DNS provider")
	f, err = dns.NewDNSProviderFactory(context.TODO(), mgr.GetClient(), *config)
	if err != nil {
		log.Err(err).Msg("Unable to create DNS provider factory")
		return err
	}

	gslbReconciler := &controllers.GslbReconciler{
		Config:             config,
		Client:             mgr.GetClient(),
		Resolver:           r,
		Scheme:             mgr.GetScheme(),
		GslbIngressHandler: controllers.NewIngressHandler(context.TODO(), mgr.GetClient(), mgr.GetScheme()),
	}

	corednsReconciler := &controllers.CoreDNSReconciler{
		Config:      config,
		Client:      mgr.GetClient(),
		Scheme:      mgr.GetScheme(),
		DNSProvider: f.Provider(),
		Bootstrap:   bootstrap,
	}

	if err = gslbReconciler.SetupWithManager(mgr); err != nil {
		log.Err(err).Msg("Unable to create Gslb reconciler")
		return err
	}

	if err = corednsReconciler.SetupWithManager(mgr); err != nil {
		log.Err(err).Msg("Unable to create coreDNS reconciler")
		return err
	}

	metrics.Metrics().SetRuntimeInfo(version, commit)

	// tracing
	cfg := tracing.Settings{
		Enabled:       config.TracingEnabled,
		Endpoint:      config.OtelExporterOtlpEndpoint,
		SamplingRatio: config.TracingSamplingRatio,
		Commit:        commit,
		AppVersion:    version,
	}
	cleanup, tracer := tracing.SetupTracing(context.Background(), cfg, log)
	gslbReconciler.Tracer = tracer
	defer cleanup()

	// +kubebuilder:scaffold:builder
	log.Info().Msg("Starting k8gb")
	if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
		log.Err(err).Msg("Problem running k8gb")
		return err
	}
	log.Info().Msg("Gracefully finished, bye!")
	return nil
}
